package com.ikingtech.platform.service.application.controller;

import com.ikingtech.framework.sdk.application.api.ApplicationApi;
import com.ikingtech.framework.sdk.application.model.ApplicationDTO;
import com.ikingtech.framework.sdk.application.model.ApplicationQueryParamDTO;
import com.ikingtech.framework.sdk.context.event.*;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.core.response.R;
import com.ikingtech.framework.sdk.enums.application.ApplicationStatusEnum;
import com.ikingtech.framework.sdk.utils.Tools;
import com.ikingtech.framework.sdk.web.annotation.ApiController;
import com.ikingtech.platform.service.application.entity.ApplicationDO;
import com.ikingtech.platform.service.application.exception.ApplicationExceptionInfo;
import com.ikingtech.platform.service.application.service.ApplicationService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.ArrayList;
import java.util.List;

import static com.ikingtech.platform.service.application.exception.ApplicationExceptionInfo.PAGE_NOT_FOUND;

/**
 * @author tie yan
 */
@Slf4j
@RequiredArgsConstructor
@ApiController(value = "/application", name = "应用中心-应用管理", description = "应用中心-应用管理")
public class ApplicationController implements ApplicationApi {

    private final ApplicationService service;

    private final ApplicationContext applicationContext;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<ApplicationDTO> add(ApplicationDTO application) {
        ApplicationDO entity = Tools.Bean.copy(application, ApplicationDO.class);
        entity.setId(Tools.Id.uuid());
        entity.setCode(Tools.Id.uuid());
        entity.setStatus(ApplicationStatusEnum.UNPUBLISHED.name());
        entity.setTenantCode(Me.tenantCode());
        this.service.save(entity);
        return R.ok(this.modelConvert(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> delete(String id) {
        ApplicationDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(PAGE_NOT_FOUND);
        }
        this.service.removeById(id);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                applicationContext.publishEvent(new ApplicationDeleteEvent(this, entity.getCode()));
            }
        });
        return R.ok();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> update(ApplicationDTO application) {
        ApplicationDO entity = this.service.getById(application.getId());
        if (null == entity) {
            throw new FrameworkException(ApplicationExceptionInfo.APPLICATION_NOT_FOUND);
        }
        ApplicationDO newEntity = Tools.Bean.copy(application, ApplicationDO.class);
        newEntity.setCode(entity.getCode());
        this.service.updateById(newEntity);
        return R.ok(this.modelConvert(newEntity));
    }

    @Override
    public R<List<ApplicationDTO>> page(ApplicationQueryParamDTO queryParam) {
        return R.ok(this.service.listPage(queryParam).convertBatch(this::modelConvert));
    }

    @Override
    public R<List<ApplicationDTO>> all() {
        return R.ok(this.modelConvert(this.service.listPublishedApplication()));
    }

    @Override
    public R<ApplicationDTO> detail(String id) {
        ApplicationDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(ApplicationExceptionInfo.APPLICATION_NOT_FOUND);
        }
        return R.ok(this.modelConvert(entity));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Object> publish(String id) {
        ApplicationDO entity = this.service.getById(id);
        if (null == entity) {
            throw new FrameworkException(ApplicationExceptionInfo.APPLICATION_NOT_FOUND);
        }
        entity.setStatus(ApplicationStatusEnum.PUBLISHED.name());
        this.service.updateById(entity);
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                applicationContext.publishEvent(new ApplicationDeleteEvent(this, entity.getCode()));
                applicationContext.publishEvent(new ApplicationPublishEvent(this, entity.getCode()));
            }
        });
        return R.ok();
    }

    @EventListener
    public void tenantInitListener(TenantInitEvent event) {
        List<ApplicationDO> entities = this.service.listPublishedApplication();
        if (Tools.Coll.isBlank(entities)) {
            return;
        }
        entities.forEach(entity ->
                this.applicationContext.publishEvent(
                        new ApplicationMenuInitEvent(this,
                                event.getCode(),
                                entity.getCode(),
                                new ArrayList<>())
                        )
                );
    }

    private List<ApplicationDTO> modelConvert(List<ApplicationDO> entities) {
        return Tools.Coll.convertList(entities, this::modelConvert);
    }

    private ApplicationDTO modelConvert(ApplicationDO entity) {
        ApplicationDTO application = Tools.Bean.copy(entity, ApplicationDTO.class);
        if (null != application.getStatus()) {
            application.setStatusName(application.getStatus().description);
        }
        if (null != application.getType()) {
            application.setTypeName(application.getType().description);
        }
        return application;
    }
}
